package com.turing.post.cardmanagement.util;

import java.util.List;

import com.turing.post.util.PCPSApplicationException;
import com.turing.post.util.PCPSUtil;

/**
 * CPU卡实用类
 * 
 * @author yxb
 * 
 */
public class AppCardApduUtil {
	/**
	 * 打开设备
	 * 
	 * @param apdulist
	 * @param full_ats
	 *            (可选,如果指定则前台控件进行验证)
	 */
	public static void openDevice(List<CardApdu> apdulist, String full_ats) {
		CardApdu apdu = new CardApdu();
		apdu.setApduCategory(CardApdu.APDU_ERR_STOP | CardApdu.APDU_OPEN);
		apdu.setApdu(PCPSUtil.isEmpty(full_ats) ? "" : full_ats);
		apdulist.add(apdu);
	}

	/**
	 * 关闭设备
	 * 
	 * @param apdulist
	 */
	public static void closeDevice(List<CardApdu> apdulist) {
		CardApdu apdu = new CardApdu();
		apdu.setApduCategory(CardApdu.APDU_ERR_STOP | CardApdu.APDU_CLOSE);
		apdu.setApdu(AppCardApduUtil.getApduClose());
		apdulist.add(apdu);
	}

	/**
	 * 检查APDU执行结果
	 * 
	 * @param apdulist
	 * @param idx
	 * @param op
	 * @param errdesc
	 * @return
	 * @throws PCPSApplicationException
	 */
	public static String getExpectedApdu(List<CardApdu> apdulist, int idx,
			int op, String errdesc) throws PCPSApplicationException {
		CardApdu apdu = apdulist.get(idx);
		idx++;
		if (apdu.getApduInstruction() != op)
			throw new PCPSApplicationException("", "非期望的APDU!");
		String result = apdu.getApduResult();
		if (PCPSUtil.isEmpty(result) || result.length() < 4
				|| !result.endsWith(CardApdu.APDU_RESPCODE_SUCC)) {
			if (!apdu.isIgnore())
				throw new PCPSApplicationException("", errdesc);
		}

		return result;
	}

	/**
	 * CLOSE
	 * 
	 * @return
	 */
	public static final String getApduClose() {
		return "CLOSE";
	}

	/**
	 * 根据协议解析ATS
	 * 
	 * @param fullATS
	 * @return
	 */
	public static final String parseATS(String fullATS) {
		int len = fullATS.length();
		if (len >= 32)
			return fullATS.substring(16, 32);
		else
			return "0000000000000000";
	}

	/**
	 * 选择CPU卡片MF300根目录APDU
	 * 
	 * @return
	 */
	public static final String getApduSelectMF300() {
		return "00A40000023F00";
	}

	/**
	 * 按文件名选择CPU卡片指定应用APDU
	 * 
	 * @return
	 */
	public static final String getApduSelectAppDir(String AID) {
		return "00A40400" + PCPSUtil.getByteHexString(AID.length() / 2) + AID;
	}

	/**
	 * 取随机数指令
	 */
	public static final String getApduGetChallenge(boolean ismac) {
		if (ismac)
			return "0084000004";
		else
			return "0084000008";
	}

	/**
	 * 写BINARY文件
	 * 
	 * @return
	 */
	public static final String getApduWriteBinary(boolean isSecurity, int SFI,
			boolean isFirstWrite, int offset, byte[] userdata) {
		StringBuilder apdu = new StringBuilder();
		apdu.append((isSecurity ? "04" : "00"));
		apdu.append("D6");
		if (isFirstWrite) {
			apdu.append(PCPSUtil.getByteHexString((0x80 | (SFI & 0x1F))));
			apdu.append(PCPSUtil.getByteHexString(offset));
		} else {
			apdu.append(PCPSUtil.getByteHexString((offset >> 8) & 0x7F));
			apdu.append(PCPSUtil.getByteHexString(offset & 0x00FF));
		}

		apdu.append(PCPSUtil.getByteHexString(userdata.length + (isSecurity ? 4 : 0)));
		apdu.append(PCPSUtil.getHexString(userdata));

		return apdu.toString();
	}

	/**
	 * 读BINARY文件
	 * 
	 * @return
	 */
	public static final String getApduReadBinary(int SFI, boolean isFirstRead,
			int offset, int len) {
		StringBuilder apdu = new StringBuilder();
		apdu.append("00B0");

		if (isFirstRead) {
			apdu.append(PCPSUtil.getByteHexString((0x80 | (SFI & 0x1F))));
			apdu.append(PCPSUtil.getByteHexString(offset));
		} else {
			apdu.append(PCPSUtil.getByteHexString((offset >> 8) & 0x7F));
			apdu.append(PCPSUtil.getByteHexString(offset & 0x00FF));
		}

		apdu.append(PCPSUtil.getByteHexString(len));

		return apdu.toString();
	}

	/**
	 * 按记录号读取指定文件的内容
	 * 
	 * @param SFI
	 * @param recno
	 * @return
	 */
	public static final String getApduReadRecord(int SFI, int recno) {
		StringBuilder apdu = new StringBuilder();
		apdu.append("00B2");
		apdu.append(PCPSUtil.getByteHexString(recno));

		apdu.append(PCPSUtil.getByteHexString(((SFI & 0x1F) << 3) | 0x04));

		apdu.append("00");

		return apdu.toString();
	}

	/**
	 * 取电子钱包余额
	 * 
	 * @return
	 */
	public static final String getApduGetEPBalance() {
		return "805C000204";
	}

	/**
	 * 创建应用目录
	 * 
	 * @param AID
	 * @return
	 */
	public static final String getApduCreatADFFile(int sfi, String AID) {
		// 80E0 0102 12 38 3F01 15 05 FAFA00 09 A00000000386980703 ;SW=9000
		StringBuilder apdu = new StringBuilder();

		// 指令分类
		apdu.append("80E0");

		// ADF -- PBOC 钱包应用
		apdu.append("0102");

		apdu.append("12");

		// 文件描述符 xx111xxx -- ADF
		apdu.append("38");

		// 文件标识 3F01
		apdu.append("3F");
		apdu.append(PCPSUtil.getByteHexString(sfi));

		apdu.append("15");

		// 属性字节2(生命周期状态) xxxxx101 -- 用户状态 -- 激活
		apdu.append("05");

		// 创建权限 -- FA(F <= M <= A, 不允许创建) 删除权限 -- FA(F <= M <= A, 不允许删除) 保留字节 --
		// 00
		apdu.append("FAFA00");

		// AID length -- 09
		apdu.append(PCPSUtil.getByteHexString(AID.length() / 2));

		// AID
		apdu.append(AID);

		return apdu.toString();
	}

	/**
	 * 创建应用EP文件
	 * 
	 * @return
	 */
	public static final String getApduCreatAppEPFile() {
		return "80E082000A01000802EF0205EFEF00";
	}

	/**
	 * 创建应用PBOC_PROOF文件
	 * 
	 * @return
	 */
	public static final String getApduCreatAppPBOCPROOFFile() {
		return "80E082000A01000B03EF0305F0F000";
	}

	/**
	 * 创建余额上限文件
	 * 
	 * @return
	 */
	public static final String getApduCreatAppBalanceLimitFile() {
		return "80E002000A01000805EF0505EFEF00";
	}

	/**
	 * 写余额上限文件
	 * 
	 * @param eplimit
	 *            电子钱包余额上限
	 * 
	 * @return
	 */
	public static final String getApduWriteAppBalanceLimitFile(int eplimit) {
		/**
		 * 存折余额无上限
		 */
		return "00D6000008FFFFFFFF" + PCPSUtil.getHexIntegerString(eplimit);
	}

	/**
	 * 创建应用电子钱包交易明细文件(0018)
	 * 
	 * @return
	 */
	public static final String getApduCreatAppEPDetailFile() {
		return "80E002000A060A1718001805EFEF00";
	}

	/**
	 * 创建应用基本信息文件(BINARY)
	 * 
	 * @return
	 */
	public static final String getApduCreatAppIssueFile(int sfi, int fid, int sz) {
		StringBuilder apdu = new StringBuilder();

		// 指令
		apdu.append("80E082000A01");

		// 记录数
		apdu.append("00");

		// 文件大小
		apdu.append(PCPSUtil.getByteHexString(sz & 0x00FF));

		// SFI
		apdu.append(PCPSUtil.getByteHexString(sfi & 0x001F));

		// FID
		apdu.append(PCPSUtil.getByteHexString((fid >> 8) & 0x00FF));
		apdu.append(PCPSUtil.getByteHexString(fid & 0x00FF));

		// 属性字节2(生命周期状态) xxxxx101 -- 用户状态 -- 激活
		apdu.append("05");

		// 读权限
		apdu.append("F0");

		// 写权限
		apdu.append("F0");

		// 线路保护
		apdu.append("20");

		return apdu.toString();
	}

	/**
	 * 创建应用KEY文件
	 * 
	 * @return
	 */
	public static final String getApduCreatAppKeyFile() {
		StringBuilder apdu = new StringBuilder();

		// 80E0 0200 0A 01 01FF 00 0000 05 EFFA 30;sw=9000

		// 指令分类
		apdu.append("80E0");

		// EF(P1,P2,P3)
		apdu.append("02000A");

		// 文件描述符 -- 二进制文件
		apdu.append("01");

		// 记录数
		apdu.append("01");

		// 记录长度 -- FF
		apdu.append("FF");

		// SFI
		apdu.append("00");

		// FID
		apdu.append("0000");

		// 属性字节2(生命周期状态) xxxxx101 -- 用户状态 -- 激活
		apdu.append("05");

		// 读权限
		apdu.append("EF");

		// 写权限
		apdu.append("FA");

		// 线路保护
		apdu.append("30");

		return apdu.toString();
	}

	/**
	 * 写KEY
	 * 
	 * @param KID
	 * @param keytype
	 * @param keyver_errcnt
	 * @param algno_status
	 * @param key
	 * @return
	 */
	public static final String getApduWriteKey(int KID, int keytype,
			int keyver_errcnt, int algno_status, String key) {
		StringBuilder apdu = new StringBuilder();

		// 主控密钥
		// 80D4 0100 15 F9 F0F0 330A 00000000000000000000000000000000;SW=9000

		// PIN密钥
		// 80D4 0100 0D 3A F0F0 3303 123456FFFFFFFFFF ;sw=9000

		// 指令分类 -- 80 -- 明文KEY
		apdu.append("80D4");

		// 01 -- 安装密钥
		apdu.append("01");

		// KID
		apdu.append(PCPSUtil.getByteHexString(KID));

		// 密钥长度
		apdu.append(PCPSUtil.getByteHexString((key.length() / 2) + 5));

		// 密钥类型
		apdu.append(PCPSUtil.getByteHexString(keytype));

		// 使用权、修改权
		apdu.append("F0F0");

		// 密钥版本/错误计数
		apdu.append(PCPSUtil.getByteHexString(keyver_errcnt));

		// 算法标识/后续状态
		apdu.append(PCPSUtil.getByteHexString(algno_status));

		// 密钥
		apdu.append(key);

		return apdu.toString();
	}

	/*
	 * //===============1001======================================================
	 * = 00A4 0400 0E 315041592E5359532E4444463032;sw=9000 00A4 0400 09
	 * A00000000386980703 ;SW=9000
	 * 
	 * 80E0 0200 0A 01 01FF 00 0000 05 EFFA 30;sw=9000 80D4 0100 15 F9 F0F0 330A
	 * 00000000000000000000000000000000;SW=9000 //主控密钥 80D4 0100 15 F6 F0F0 3300
	 * 00000000000000000000000000000000;sw=9000 //维护
	 * 
	 * 80D4 0101 15 E7 F0F0 0100 00000000000000000000000000000000;SW=9000 //消费密钥
	 * 80D4 0102 15 E7 F0F0 0100 00000000000000000000000000000000;sw=9000 //
	 * 80D4 0103 15 E7 F0F0 0100 00000000000000000000000000000000;SW=9000 //
	 * 80D4 0104 15 E7 F0F0 0100 00000000000000000000000000000000;sw=9000 //
	 * 80D4 0101 15 E6 F0F0 0100 00000000000000000000000000000000;SW=9000 //圈存
	 * 80D4 0102 15 E6 F0F0 0100 00000000000000000000000000000000;sw=9000 //
	 * 80D4 0100 15 E5 F0F0 0100 00000000000000000000000000000000;SW=9000 //TAC
	 * 80D4 0100 15 F8 F0F0 0100 00000000000000000000000000000000;SW=9000
	 * //Pin重装 80D4 0100 15 F7 F0F0 0100
	 * 00000000000000000000000000000000;SW=9000 //Pin解锁 80D4 0100 0D 3A F0F0
	 * 3303 123456FFFFFFFFFF ;sw=9000 //PIN
	 * 
	 * 80E0 8200 0A 01 00D3 15 0015 05 F0F0 20;sw=9000 //公共应用基本信息文件 80E0 8200 0A
	 * 01 0008 02 EF02 05 EFEF 00;sw=9000 //EP钱包文件(二进制) 80E0 8200 0A 01 000B 03
	 * EF03 05 F0F0 00;sw=9000 //PBOC_PROOF文件(二进制) 80E0 0200 0A 01 0008 05 EF05
	 * 05 EFEF 00;sw=9000 //余额上限文件 00D6 0000 08 FFFFFFFF00FFFFFF ;SW=9000 80E0
	 * 0200 0A 06 0A17 18 0018 05 EFEF 00;sw=9000 //电子钱包交易明细记录文件（0018） 80E0 8200
	 * 0A 06 041F 1A 001A 05 F0EF 00 ;SW=9000 //电子钱包交易充值明细记录文件（001A）
	 */

	/**
	 * 判断圈存的终端号是否合法
	 * 
	 * @param terminalNo
	 * @return
	 */
	public static final boolean isValidateTerminal(String terminalNo) {
		if (PCPSUtil.isEmpty(terminalNo))
			return false;
		if (terminalNo.length() != 12)
			return false;
		char[] sch = terminalNo.toCharArray();

		// 终端号必须为12为的数字
		for (char sc : sch) {
			if (sc < '0' || sc > '9')
				return false;
		}

		return true;
	}

	/**
	 * 产生圈存初始化指令
	 * 
	 * @param keyIdx
	 * @param amount
	 * @param terminalNo
	 * @return
	 */
	public static final String getApduLoadInit(int keyIdx, int amount,
			String terminalNo) {
		StringBuilder apdu = new StringBuilder();
		apdu.append("805000020B");
		apdu.append(PCPSUtil.getByteHexString(keyIdx));
		apdu.append(PCPSUtil.getByteHexString((amount >> 24) & 0x0FF));
		apdu.append(PCPSUtil.getByteHexString((amount >> 16) & 0x0FF));
		apdu.append(PCPSUtil.getByteHexString((amount >> 8) & 0x0FF));
		apdu.append(PCPSUtil.getByteHexString(amount & 0x0FF));
		apdu.append(terminalNo);
		apdu.append("10");

		return apdu.toString();
	}

	/**
	 * 产生圈存指令
	 * 
	 * @param tranDate
	 * @param tranTime
	 * @param mac2
	 * @return
	 */
	public static final String getApduLoad(String tranDate, String tranTime,
			String mac2) {
		StringBuilder apdu = new StringBuilder();
		apdu.append("805200000B");
		apdu.append(tranDate);
		apdu.append(tranTime);
		apdu.append(mac2);
		apdu.append("04");

		return apdu.toString();
	}

	public static void main(String[] args) throws Exception {
		System.out.println(getApduReadBinary(0x05, true, 0, 0));
		System.out.println(getApduReadRecord(0x18, 1));

		String detail = "0056000000000000640671000100406620130403143959";
		byte[] rd = PCPSUtil.getBytes(detail);
		CardEPDetail cd = CardEPDetail.parseEPDetail(rd);
		System.out.println("transeqno: " + cd.getTranSeqNo());
		System.out.println("overdraftlimit: " + cd.getOverdraftLimit());
		System.out.println("amount: " + cd.getAmount());
		System.out.println("trantype: " + cd.getTranType());
		System.out.println("trantypedesc: " + cd.getTranTypeDesc());
		System.out.println("terminalno: " + cd.getTerminalNo());
		System.out.println("tranDate: " + cd.getTranDate());
		System.out.println("tranTime: " + cd.getTranTime());

		detail = "01961850961850123456789ABC";

		CardIssueRoot cir = CardIssueRoot.parseCardIssueRoot(detail);
		System.out.println("version: " + cir.getVersion());
		System.out.println("issuecompanyno: " + cir.getIssueCompanyNo());
		System.out.println("cardseqno: " + cir.getCardSeqNo());
	}
}
